/* eslint-disable max-len */
import ruleTrigger from './ruleTrigger'

let confGlobal
let someSpanIsNot24

export function dialogWrapper(str) {
	return `<el-dialog v-bind="$attrs" v-on="$listeners" @open="onOpen" @close="onClose" title="Dialog Titile">
    ${str}
    <div slot="footer">
      <el-button @click="close">取消</el-button>
      <el-button type="primary" @click="handelConfirm">确定</el-button>
    </div>
  </el-dialog>`
}

export function vueTemplate(str, type) {
	let tem = ''
	if (type === 'vue') {
		tem = `<template>
    <div>
      ${str}
    </div>
  </template>`
	} else if (type === 'html') {
		tem = `
    <div id="app">
      ${str}
    </div>
  `
	}
	return tem
}

export function vueScript(str, formData, type) {
	let jsTem = ''
	if (type === 'vue') {
		jsTem = `
    <script>
        ${str}
    </script>
  `
	}
	if (type === 'html' && formData) {
		const res = formData.fields.findIndex(i => i.__config__.tag === 'tinymce')
		if (res > 0) {
			jsTem = `
    <script>
    window.debounce = function (delay, noTrailing, callback, debounceMode) {
      let timeoutID;
      let cancelled = false;

      let lastExec = 0;

      // Function to clear existing timeout
      function clearExistingTimeout() {
        if (timeoutID) {
          clearTimeout(timeoutID);
        }
      }

      // Function to cancel next exec
      function cancel() {
        clearExistingTimeout();
        cancelled = true;
      }

      if (typeof noTrailing !== 'boolean') {
        debounceMode = callback;
        callback = noTrailing;
        noTrailing = undefined;
      }

      function wrapper(...arguments_) {
        let self = this;
        let elapsed = Date.now() - lastExec;

        if (cancelled) {
          return;
        }

        function exec() {
          lastExec = Date.now();
          callback.apply(self, arguments_);
        }
        function clear() {
          timeoutID = undefined;
        }

        if (debounceMode && !timeoutID) {
          exec();
        }

        clearExistingTimeout();

        if (debounceMode === undefined && elapsed > delay) {
          exec();
        } else if (noTrailing !== true) {
          timeoutID = setTimeout(
            debounceMode ? clear : exec,
            debounceMode === undefined ? delay - elapsed : delay
          );
        }
      }

      wrapper.cancel = cancel;

      // Return the wrapper function.
      return wrapper;
    }
  </script>
    <script>
    const plugins = [
      'advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textpattern visualblocks visualchars wordcount'
    ]
    const toolbar = [
      'code searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote removeformat subscript superscript codesample hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'
    ]
    const callbacks = {}
    function loadScript(src, callback) {
      const existingScript = document.getElementById(src)
      const cb = callback || (() => { })
      if (!existingScript) {
        callbacks[src] = []
        const $script = document.createElement('script')
        $script.src = src
        $script.id = src
        $script.async = 1
        document.body.appendChild($script)
        const onEnd = 'onload' in $script ? stdOnEnd.bind($script) : ieOnEnd.bind($script)
        onEnd($script)
      }

      callbacks[src].push(cb)

      function stdOnEnd(script) {
        script.onload = () => {
          this.onerror = this.onload = null
          callbacks[src].forEach(item => {
            item(null, script)
          })
          delete callbacks[src]
        }
        script.onerror = () => {
          this.onerror = this.onload = null
          cb(new Error('Failed to load' + src), script)
        }
      }

      function ieOnEnd(script) {
        script.onreadystatechange = () => {
          if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
          this.onreadystatechange = null
          callbacks[src].forEach(item => {
            item(null, script)
          })
          delete callbacks[src]
        }
      }
    }
    function loadTinymce(cb) {
      let tinymceUrl = "/static/vendor/tinymce/tinymce.min.js"
      const loading = window.ELEMENT.Loading.service({
        fullscreen: true,
        lock: true,
        text: '富文本资源加载中...',
        spinner: 'el-icon-loading',
        background: 'rgba(255, 255, 255, 0.5)'
      })

      loadScript(tinymceUrl, () => {
        loading.close()
        cb(tinymce)
      })
    }
    let num = 1
    Vue.component('tinymce', {
      props: {
        id: {
          type: String,
          default: () => {
            num === 10000 && (num = 1)
            return 'tinymce'+ +new Date() + num++
          }
        },
        value: {
          default: ''
        }
      },
      data() {
        return {
          tinymceId: this.id
        }
      },
      mounted() {
        window.loadTinymce(tinymce => {
          loadScript("/static/vendor/tinymce/zh_CN.js")
          let conf = {
            selector: '#'+ this.tinymceId,
            language: 'zh_CN',
            menubar: 'file edit insert view format table',
            plugins,
            toolbar,
            height: 300,
            branding: false,
            object_resizing: false,
            end_container_on_empty_block: true,
            powerpaste_word_import: 'clean',
            code_dialog_height: 450,
            code_dialog_width: 1000,
            advlist_bullet_styles: 'square',
            advlist_number_styles: 'default',
            default_link_target: '_blank',
            link_title: false,
            nonbreaking_force_tab: true
          }
          conf = Object.assign(conf, this.$attrs)
          conf.init_instance_callback = editor => {
            if (this.value) editor.setContent(this.value)
            this.vModel(editor)
          }
          tinymce.init(conf)
        })
      },
      destroyed() {
        this.destroyTinymce()
      },
      methods: {
        vModel(editor) {
          // 控制连续写入时setContent的触发频率
          const debounceSetContent = debounce(250, editor.setContent,false)
          this.$watch('value', (val, prevVal) => {
            if (editor && val !== prevVal && val !== editor.getContent()) {
              if (typeof val !== 'string') val = val.toString()
              debounceSetContent.call(editor, val)
            }
          })

          editor.on('change keyup undo redo', () => {
            this.$emit('input', editor.getContent())
          })
        },
        destroyTinymce() {
          if (!window.tinymce) return
          const tinymce = window.tinymce.get(this.tinymceId)
          if (tinymce) {
            tinymce.destroy()
          }
        }
      },
      template: '<textarea :id="tinymceId" style="visibility: hidden" />'
    })
      var formGeneratorHtml = new Vue(
        ${str}
      )
    </script>
  `
		} else {
			jsTem = `
			<script>
				var formGeneratorHtml =	new Vue(${str})
			</script>
			`
		}
	}
	return jsTem
}

export function cssStyle(cssStr) {
	return `<style>
    ${cssStr}
  </style>`
}
export function vueToHtml(script, css, html) {
	return `
    <html>
    <head>
      <meta charset="UTF-8">
      ${css}
    </head>
    <body>
      ${html}
      ${script}
    </body>
    </html>`
}

function buildFormTemplate(scheme, child, type) {
	let labelPosition = ''
	if (scheme.labelPosition !== 'right') {
		labelPosition = `label-position="${scheme.labelPosition}"`
	}
	const disabled = scheme.disabled ? `:disabled="${scheme.disabled}"` : ''
	let str = `<el-form ref="${scheme.formRef}" :model="${
		scheme.formModel
	}" :rules="${scheme.formRules}" size="${
		scheme.size
	}" ${disabled} label-width="${scheme.labelWidth}px" ${labelPosition}>
      ${child}
      ${buildFromBtns(scheme, type)}
    </el-form>`
	if (someSpanIsNot24) {
		str = `<el-row :gutter="${scheme.gutter}">
        ${str}
      </el-row>`
	}
	return str
}

function buildFromBtns(scheme, type) {
	// console.log(scheme, type, 111)
	let str = ''
	if (scheme.formBtns && type) {
		str = `<el-form-item size="large">
          <el-button type="primary" @click="submitForm">提交</el-button>
          <el-button @click="resetForm">重置</el-button>
        </el-form-item>`
		if (someSpanIsNot24) {
			str = `<el-col :span="24">
          ${str}
        </el-col>`
		}
	}
	return str
}

// span不为24的用el-col包裹
function colWrapper(scheme, str) {
	if (someSpanIsNot24 || scheme.__config__.span !== 24) {
		return `<el-col :span="${scheme.__config__.span}">
      ${str}
    </el-col>`
	}
	return str
}

const layouts = {
	colFormItem(scheme) {
		const config = scheme.__config__
		let labelWidth = ''
		let label = `label="${config.label}"`
		if (config.labelWidth && config.labelWidth !== confGlobal.labelWidth) {
			labelWidth = `label-width="${config.labelWidth}px"`
		}
		if (config.showLabel === false) {
			labelWidth = 'label-width="0"'
			label = ''
		}
		const required =
			!ruleTrigger[config.tag] && config.required ? 'required' : ''
		const tagDom = tags[config.tag] ? tags[config.tag](scheme) : null
		let str = `<el-form-item ${labelWidth} ${label} prop="${scheme.__vModel__}" ${required}>
        ${tagDom}
      </el-form-item>`
		str = colWrapper(scheme, str)
		return str
	},
	rowFormItem(scheme) {
		const config = scheme.__config__
		const type = scheme.type === 'default' ? '' : `type="${scheme.type}"`
		const justify =
			scheme.type === 'default' ? '' : `justify="${scheme.justify}"`
		const align = scheme.type === 'default' ? '' : `align="${scheme.align}"`
		const gutter = scheme.gutter ? `:gutter="${scheme.gutter}"` : ''
		const children = config.children.map(el =>
			layouts[el.__config__.layout](el),
		)
		let str = `<el-row ${type} ${justify} ${align} ${gutter}>
      ${children.join('\n')}
    </el-row>`
		str = colWrapper(scheme, str)
		return str
	},
}

const tags = {
	'el-button': el => {
		const { tag, disabled } = attrBuilder(el)
		const type = el.type ? `type="${el.type}"` : ''
		const icon = el.icon ? `icon="${el.icon}"` : ''
		const round = el.round ? 'round' : ''
		const size = el.size ? `size="${el.size}"` : ''
		const plain = el.plain ? 'plain' : ''
		const circle = el.circle ? 'circle' : ''
		let child = buildElButtonChild(el)

		if (child) child = `\n${child}\n` // 换行
		return `<${tag} ${type} ${icon} ${round} ${size} ${plain} ${disabled} ${circle}>${child}</${tag}>`
	},
	'el-input': el => {
		const { tag, disabled, vModel, clearable, placeholder, width } =
			attrBuilder(el)
		const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
		const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''
		const readonly = el.readonly ? 'readonly' : ''
		const prefixIcon = el['prefix-icon']
			? `prefix-icon='${el['prefix-icon']}'`
			: ''
		const suffixIcon = el['suffix-icon']
			? `suffix-icon='${el['suffix-icon']}'`
			: ''
		const showPassword = el['show-password'] ? 'show-password' : ''
		const type = el.type ? `type="${el.type}"` : ''
		const autosize =
			el.autosize && el.autosize.minRows
				? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"`
				: ''
		let child = buildElInputChild(el)

		if (child) child = `\n${child}\n` // 换行
		return `<${tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${tag}>`
	},
	'el-input-number': el => {
		const { tag, disabled, vModel, placeholder } = attrBuilder(el)
		const controlsPosition = el['controls-position']
			? `controls-position=${el['controls-position']}`
			: ''
		const min = el.min >= 0 ? `:min='${el.min}'` : ''
		const max = el.max >= 0 ? `:max='${el.max}'` : ''
		const step = el.step ? `:step='${el.step}'` : ''
		const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''
		const precision = el.precision ? `:precision='${el.precision}'` : ''

		return `<${tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${tag}>`
	},
	'el-select': el => {
		const { tag, disabled, vModel, clearable, placeholder, width } =
			attrBuilder(el)
		const filterable = el.filterable ? 'filterable' : ''
		const multiple = el.multiple ? 'multiple' : ''
		let child = buildElSelectChild(el)

		if (child) child = `\n${child}\n` // 换行
		return `<${tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${tag}>`
	},
	'table-select': el => {
		// console.log(el, 1111)
		const { tag, disabled, vModel, clearable, placeholder, width } =
			attrBuilder(el)
		const filterable = el.filterable ? 'filterable' : ''
		const multiple = el.multiple ? 'multiple' : ''
		// const options = el.props ? el.props : {}
		let child = buildElSelectChild(el)
		if (child) child = `\n${child}\n` // 换行
		return `<${tag} ${vModel} v-bind='${el.__vModel__}Props' ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${tag}>`
	},
	'el-radio-group': el => {
		const { tag, disabled, vModel } = attrBuilder(el)
		const size = `size="${el.size}"`
		let child = buildElRadioGroupChild(el)

		if (child) child = `\n${child}\n` // 换行
		return `<${tag} ${vModel} ${size} ${disabled}>${child}</${tag}>`
	},
	'el-checkbox-group': el => {
		const { tag, disabled, vModel } = attrBuilder(el)
		const size = `size="${el.size}"`
		const min = el.min ? `:min="${el.min}"` : ''
		const max = el.max ? `:max="${el.max}"` : ''
		let child = buildElCheckboxGroupChild(el)

		if (child) child = `\n${child}\n` // 换行
		return `<${tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${tag}>`
	},
	'el-switch': el => {
		const { tag, disabled, vModel } = attrBuilder(el)
		const activeText = el['active-text']
			? `active-text="${el['active-text']}"`
			: ''
		const inactiveText = el['inactive-text']
			? `inactive-text="${el['inactive-text']}"`
			: ''
		const activeColor = el['active-color']
			? `active-color="${el['active-color']}"`
			: ''
		const inactiveColor = el['inactive-color']
			? `inactive-color="${el['inactive-color']}"`
			: ''
		const activeValue =
			el['active-value'] !== true
				? `:active-value='${JSON.stringify(el['active-value'])}'`
				: ''
		const inactiveValue =
			el['inactive-value'] !== false
				? `:inactive-value='${JSON.stringify(el['inactive-value'])}'`
				: ''

		return `<${tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${tag}>`
	},
	'el-cascader': el => {
		const { tag, disabled, vModel, clearable, placeholder, width } =
			attrBuilder(el)
		const options = el.options ? `:options="${el.__vModel__}Options"` : ''
		const props = el.props ? `:props="${el.__vModel__}Props"` : ''
		const showAllLevels = el['show-all-levels']
			? ''
			: ':show-all-levels="false"'
		const filterable = el.filterable ? 'filterable' : ''
		const separator = el.separator === '/' ? '' : `separator="${el.separator}"`

		return `<${tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${tag}>`
	},
	'el-slider': el => {
		const { tag, disabled, vModel } = attrBuilder(el)
		const min = el.min ? `:min='${el.min}'` : ''
		const max = el.max ? `:max='${el.max}'` : ''
		const step = el.step ? `:step='${el.step}'` : ''
		const range = el.range ? 'range' : ''
		const showStops = el['show-stops']
			? `:show-stops="${el['show-stops']}"`
			: ''

		return `<${tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${tag}>`
	},
	'el-time-picker': el => {
		const { tag, disabled, vModel, clearable, placeholder, width } =
			attrBuilder(el)
		const startPlaceholder = el['start-placeholder']
			? `start-placeholder="${el['start-placeholder']}"`
			: ''
		const endPlaceholder = el['end-placeholder']
			? `end-placeholder="${el['end-placeholder']}"`
			: ''
		const rangeSeparator = el['range-separator']
			? `range-separator="${el['range-separator']}"`
			: ''
		const isRange = el['is-range'] ? 'is-range' : ''
		const format = el.format ? `format="${el.format}"` : ''
		const valueFormat = el['value-format']
			? `value-format="${el['value-format']}"`
			: ''
		const pickerOptions = el['picker-options']
			? `:picker-options='${JSON.stringify(el['picker-options'])}'`
			: ''

		return `<${tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${tag}>`
	},
	'el-date-picker': el => {
		const { tag, disabled, vModel, clearable, placeholder, width } =
			attrBuilder(el)
		const startPlaceholder = el['start-placeholder']
			? `start-placeholder="${el['start-placeholder']}"`
			: ''
		const endPlaceholder = el['end-placeholder']
			? `end-placeholder="${el['end-placeholder']}"`
			: ''
		const rangeSeparator = el['range-separator']
			? `range-separator="${el['range-separator']}"`
			: ''
		const format = el.format ? `format="${el.format}"` : ''
		const valueFormat = el['value-format']
			? `value-format="${el['value-format']}"`
			: ''
		const type = el.type === 'date' ? '' : `type="${el.type}"`
		const readonly = el.readonly ? 'readonly' : ''

		return `<${tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${tag}>`
	},
	'el-rate': el => {
		const { tag, disabled, vModel } = attrBuilder(el)
		const max = el.max ? `:max='${el.max}'` : ''
		const allowHalf = el['allow-half'] ? 'allow-half' : ''
		const showText = el['show-text'] ? 'show-text' : ''
		const showScore = el['show-score'] ? 'show-score' : ''

		return `<${tag} ${vModel} ${max} ${allowHalf} ${showText} ${showScore} ${disabled}></${tag}>`
	},
	'el-color-picker': el => {
		const { tag, disabled, vModel } = attrBuilder(el)
		const size = `size="${el.size}"`
		const showAlpha = el['show-alpha'] ? 'show-alpha' : ''
		const colorFormat = el['color-format']
			? `color-format="${el['color-format']}"`
			: ''

		return `<${tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${tag}>`
	},
	'el-upload': el => {
		const { tag } = el.__config__
		const disabled = el.disabled ? ":disabled='true'" : ''
		const action = el.action ? `:action="${el.__vModel__}Action"` : ''
		const multiple = el.multiple ? 'multiple' : ''
		const listType =
			el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
		const accept = el.accept ? `accept="${el.accept}"` : ''
		const name = el.name !== 'file' ? `name="${el.name}"` : ''
		const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : ''
		const beforeUpload = `:before-upload="${el.__vModel__}BeforeUpload"`
		const fileList = `:file-list="${el.__vModel__}fileList"`
		const ref = `ref="${el.__vModel__}"`
		let child = buildElUploadChild(el)

		if (child) child = `\n${child}\n` // 换行
		return `<${tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${tag}>`
	},
	tinymce: el => {
		const { tag, vModel, placeholder } = attrBuilder(el)
		const height = el.height ? `:height="${el.height}"` : ''
		const branding = el.branding ? `:branding="${el.branding}"` : ''
		return `<${tag} ${vModel} ${placeholder} ${height} ${branding}></${tag}>`
	},
}

function attrBuilder(el) {
	return {
		tag: el.__config__.tag,
		vModel: `v-model="${confGlobal.formModel}.${el.__vModel__}"`,
		clearable: el.clearable ? 'clearable' : '',
		placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '',
		width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '',
		disabled: el.disabled ? ":disabled='true'" : '',
	}
}

// el-buttin 子级
function buildElButtonChild(scheme) {
	const children = []
	const slot = scheme.__slot__ || {}
	if (slot.default) {
		children.push(slot.default)
	}
	return children.join('\n')
}

// el-input 子级
function buildElInputChild(scheme) {
	const children = []
	const slot = scheme.__slot__
	if (slot && slot.prepend) {
		children.push(`<template slot="prepend">${slot.prepend}</template>`)
	}
	if (slot && slot.append) {
		children.push(`<template slot="append">${slot.append}</template>`)
	}
	return children.join('\n')
}

// el-select 子级
function buildElSelectChild(scheme) {
	const children = []
	const slot = scheme.__slot__
	if (slot && slot.options && slot.options) {
		children.push(
			`<el-option v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`,
		)
	}
	return children.join('\n')
}

// el-radio-group 子级
function buildElRadioGroupChild(scheme) {
	const children = []
	const slot = scheme.__slot__
	const config = scheme.__config__
	if (slot && slot.options && slot.options.length) {
		const tag = config.optionType === 'button' ? 'el-radio-button' : 'el-radio'
		const border = config.border ? 'border' : ''
		children.push(
			`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`,
		)
	}
	return children.join('\n')
}

// el-checkbox-group 子级
function buildElCheckboxGroupChild(scheme) {
	const children = []
	const slot = scheme.__slot__
	const config = scheme.__config__
	if (slot && slot.options && slot.options.length) {
		const tag =
			config.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
		const border = config.border ? 'border' : ''
		children.push(
			`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`,
		)
	}
	return children.join('\n')
}

// el-upload 子级
function buildElUploadChild(scheme) {
	const list = []
	const config = scheme.__config__
	if (scheme['list-type'] === 'picture-card')
		list.push('<i class="el-icon-plus"></i>')
	else
		list.push(
			`<el-button size="small" type="primary" icon="el-icon-upload">${config.buttonText}</el-button>`,
		)
	if (config.showTip)
		list.push(
			`<div slot="tip" class="el-upload__tip">只能上传不超过 ${config.fileSize}${config.sizeUnit} 的${scheme.accept}文件</div>`,
		)
	return list.join('\n')
}

/**
 * 组装html代码。【入口函数】
 * @param {Object} formConfig 整个表单配置
 * @param {String} type 生成类型，文件或弹窗等
 */
export function makeUpHtml(formConfig, type) {
	const htmlList = []
	confGlobal = formConfig
	// 判断布局是否都沾满了24个栅格，以备后续简化代码结构
	someSpanIsNot24 = formConfig.fields.some(item => item.__config__.span !== 24)
	// 遍历渲染每个组件成html
	formConfig.fields.forEach(el => {
		htmlList.push(layouts[el.__config__.layout](el))
	})
	const htmlStr = htmlList.join('\n')
	// 将组件代码放进form标签
	let temp = buildFormTemplate(formConfig, htmlStr, type)
	// dialog标签包裹代码
	if (type === 'dialog') {
		temp = dialogWrapper(temp)
	}
	confGlobal = null
	return temp
}
